#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>

static const int gmax_retries = 5;
static const int gretries_interval = 1;
static const int gsockfd_default = -1;

enum class State
{
    NEW,        // 新连接
    CONNECT,    // 链接成功
    DISCONNECT, // 连接/重连失败
    CLOSE       // 所有重连失败
};

enum Error
{
    SOCKET_ERROR,
    RECONNECT_ERR
};

class ClientConnect
{
public:
    ClientConnect(std::string ser_ip, int ser_port)
        : _ser_ip(ser_ip), _ser_port(ser_port), _sockfd(gsockfd_default), _state(State::NEW)
    {
    }

    void Connect() // 首次连接
    {
        // 打开网卡
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        std::cout << "_sockfd " << _sockfd << std::endl;
        if (_sockfd < 0)
        {
            std::cerr << "socket fail" << std::endl;
            exit(SOCKET_ERROR);
        }

        // 创建并填充ser_socket对象
        struct sockaddr_in ser_socket;
        memset(&ser_socket, 0, sizeof(ser_socket));
        uint len = sizeof(ser_socket);
        ser_socket.sin_family = AF_INET;
        ser_socket.sin_port =htons(_ser_port);
        ::inet_pton(AF_INET, _ser_ip.c_str(), &ser_socket.sin_addr);
        // connect发起连接
        int n = connect(_sockfd, (struct sockaddr *)&ser_socket, len);
        std::cout << "n " << n << std::endl;
        if (n < 0)
        {
            // connect fail
            std::cout << "connect fail" << std::endl;
            Disconnect(); // 连接失败时的操作
            _state = State::DISCONNECT;
            return;
        }
        // connect success
        _state = State::CONNECT;
        return;
    }

    void Reconnect() // 重连
    {
        std::cerr << "222" << std::endl;

        int cnt = 1;
        while (cnt <= gmax_retries)
        {
            std::cerr << "333" << std::endl;

            Connect();
            std::cout << "retries " << cnt << " 次 fail" << std::endl;
            std::cout << (int)_state << std::endl;
            std::cout << (int)State::CONNECT << std::endl;
            if (_state == State::CONNECT)
            {
                std::cout << "第 retries " << cnt << " 次 success" << std::endl;
                break;
            }
            cnt++;
            sleep(gretries_interval);
        }
        _state = State::CLOSE;
        exit(RECONNECT_ERR);
    }

    void Disconnect() // 连接失败
    {
        if (_sockfd != gsockfd_default)
        {
            close(_sockfd);
            _state = State::CLOSE;
            _sockfd = gsockfd_default;
        }
    }
    void Process()
    {
        // // connect发起连接
        // Connect();
        // std::cerr << "111" << std::endl;
        // if (_state == State::DISCONNECT)
        // {
        //     Reconnect(); // 重连
        // }

        // send
        std::string in;
        std::getline(std::cin, in);
        int n = send(_sockfd, in.c_str(), in.size(), 0);
        if (n < 0)
        {
            std::cerr << "send fail" << std::endl;
        }
        // recv
        char buffer[1024] = {0};
        n = recv(_sockfd, buffer, sizeof(buffer) - 1, 0); // 留一个空给\0
        if (n > 0)
        {
            buffer[n] = '\0';
            std::cout << "recv:" << buffer << std::endl;
        }
        else if (n == 0) // server quit
        {
            std::cout << "server quit" << std::endl;
        }
        else
        {
            std::cerr << "recv fail" << std::endl;
        }
    }

    State GetState()
    {
        return _state;
    }

    int GetSockfd()
    {
        return _sockfd;
    }

    ~ClientConnect()
    {
        _state = State::CLOSE;
        Disconnect();
    }

private:
    State _state;
    std::string _ser_ip;
    int _ser_port;
    int _sockfd;
};

class TcpClient
{
public:
    TcpClient(std::string ser_ip, int ser_port)
        : _ser_ip(ser_ip), _ser_port(ser_port), _clientconnect(ser_ip, ser_port)
    {
    }
    void Execute()
    {
        while (true)
        {
            switch (_clientconnect.GetState())
            {
            case State::NEW:
            {
                _clientconnect.Connect();
                break;
            }
            case State::CONNECT:
            {
                _clientconnect.Process();
                break;
            }
            case State::DISCONNECT:
            {
                _clientconnect.Reconnect();
                break;
            }
            case State::CLOSE:
            {
                break;
            }
            default:
                break;
            }
        }
        _clientconnect.Process();
    }

private:
    std::string _ser_ip;
    int _ser_port;
    ClientConnect _clientconnect;
};

//./client
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        std::cerr << "./client serverip serverport" << std::endl;
        exit(-1);
    }
    std::string ser_ip = argv[1];
    int ser_port = std::stoi(argv[2]);

    TcpClient client(ser_ip, ser_port);
    client.Execute();

    return 0;
}
